
#include <BStyle>
#include <BIcon>
#include <BPainter>
#include <BMouseEvent>
#include <BSlider>
#include <BSpinBox>
#include <BButton>
#include <BLabel>
#include <BColorDialog>

#include "PixmapArea.h"
#include "EraseOperation.h"

EraseOperation::EraseOperation() : Operation("Erase")
{
	_layout = new BHBoxLayout();
	{
		_eraseLabel = new BLabel("erase");
		_sizeSlider = new BSlider(1, 100);
		_sizeSpinBox = new BSpinBox(1, 100);
		_colorButton = new BButton(Graph_Square_Fill);
		_colorButton->setSizePolicy(Policy_Preferred);
		_colorButton->setCheckable(true);

		_layout->addWidget(_eraseLabel);
		_layout->addWidget(_sizeSlider);
		_layout->addWidget(_sizeSpinBox);
		_layout->addWidget(_colorButton);
	}

	connect(_sizeSlider, Signal_Sliding, &EraseOperation::slotRadiusSliding);
	connect(_sizeSpinBox, Signal_EditFinished, &EraseOperation::slotRadiusEditFinished);
	connect(_sizeSpinBox, Signal_ValueChanged, &EraseOperation::slotRadiusChanged);
	connect(_colorButton, Signal_Clicked, &EraseOperation::slotColorButtonClicked);
	_sizeSlider->setValue(10);
	_sizeSpinBox->setValue(10);
}
EraseOperation::~EraseOperation()
{

}

void EraseOperation::slotRadiusSliding(BObject* object, const BValue& value)
{
	int radius = value;
	_sizeSpinBox->setValue(radius);
}
void EraseOperation::slotRadiusEditFinished(BObject* object, const BValue& value)
{
	int radius = value;
	_sizeSlider->setValue(radius);
}
void EraseOperation::slotRadiusChanged(BObject* object, const BValue& value)
{
	this->fresh();
}
void EraseOperation::slotColorButtonClicked()
{
	BColor color = BColorDialog::ChooseColor("Choose color", _colorButton->color());
	_colorButton->setColor(color);
}

void EraseOperation::attached(BWidget* widget)
{
	widget->addLayout(_layout);
	Operation::attached(widget);
}
void EraseOperation::detached(BWidget* widget)
{
	widget->removeLayout(_layout);
	Operation::detached(widget);
}

void EraseOperation::paint(BPainter& painter)
{
	PixmapArea* pixmapArea = this->pixmapArea();
	int circleRadius = _sizeSpinBox->value();
	if (circleRadius > 2)
	{
		painter.setLineSmooth(true);
		painter.setLineWidth(2);
		if (_pressed)
			painter.setColor(55, 0, 220);
		else
			painter.setColor(55, 0, 220);
		painter.drawCircle(_eraseCenter, circleRadius);
	}
}
void EraseOperation::update(double time)
{
	if (_pressed)
	{
		PixmapArea* pixmapArea = this->pixmapArea();
		BColor eraseColor = _colorButton->color();
		int eraseRadius = _sizeSpinBox->value();
		BRect eraseRect;
		eraseRect.reset(_eraseCenter);
		eraseRect.left() -= eraseRadius;
		eraseRect.right() += eraseRadius;
		eraseRect.top() -= eraseRadius;
		eraseRect.bottom() += eraseRadius;
		eraseRect = pixmapArea->mapToPixmap(eraseRect);
		BReal radius = eraseRect.width() / 2;

		BPixmap* pixmap = this->pixmap();
		for (int i = 0; i < pixmap->widgetCount(); i++)
		{
			ImageLayer* layer = (ImageLayer*)pixmap->widget(i);
			if (!layer->visible())
				continue;
			BImage* image = layer->image();
			BRect rect = eraseRect - layer->pos();
			BPoint center = rect.center();
			rect &= BRect(0, 0, image->width(), image->height());
			if (rect.empty())
				continue;
			for (int y = rect.top(); y < rect.bottom(); y++)
			{
				BByte* rowBytes = image->bytes(y);
				for (int x = rect.left(); x < rect.right(); x++)
				{
					BReal length = (center - BPoint(x, y)).length();
					BReal ratio = (radius - length) / radius;
					if (ratio < 0)
						continue;
					BColor color = eraseColor;
					if (image->format() == Format_RGB)
					{
						BByte* pixel = rowBytes + x * 3;
						pixel[0] = BByte(pixel[0] * (1.0 - ratio) + color.r() * ratio);
						pixel[1] = BByte(pixel[1] * (1.0 - ratio) + color.g() * ratio);
						pixel[2] = BByte(pixel[2] * (1.0 - ratio) + color.b() * ratio);
					}
					else if (image->format() == Format_RGBA)
					{
						BByte* pixel = rowBytes + x * 4;
						pixel[0] = BByte(pixel[0] * (1.0 - ratio) + color.r() * ratio);
						pixel[1] = BByte(pixel[1] * (1.0 - ratio) + color.g() * ratio);
						pixel[2] = BByte(pixel[2] * (1.0 - ratio) + color.b() * ratio);
						pixel[3] = BByte(pixel[3] * (1.0 - ratio) + color.a() * ratio);
					}
				}
			}
			image->dirty();
		}
	}
}

void EraseOperation::freshEvent(const BEvent& event)
{
	BSize hsize = _layout->sizeHint();
	BRect crect = this->pixmapArea()->centerRect();
	BRect layoutRect = crect.align(Align_RightBottom, hsize);
	_layout->setRect(layoutRect);
	_layout->adjust();
}

bool EraseOperation::focusEvent(const BEvent& event)
{
	return false;
}
bool EraseOperation::keyDownEvent(const BKeyEvent& keyEvent)
{
	return false;
}
bool EraseOperation::keyUpEvent(const BKeyEvent& keyEvent)
{
	return false;
}

bool EraseOperation::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool EraseOperation::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool EraseOperation::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pressed = true;
		return true;
	}
	return false;
}
bool EraseOperation::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pressed = false;
		return true;
	}
	return false;
}
bool EraseOperation::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	PixmapArea* pixmapArea = this->pixmapArea();
	BImage* image = pixmapArea->image();
	_eraseCenter = mouseEvent.pos();
	this->fresh();
	return false;
}
